
import {debugUtils} from '../debugUtils';
import {globalCache} from "../utils/globalCache";


// 用来渲染2d
class Graphics2d {

    constructor(imgData) {
        this.imgData = imgData;
        this.width = imgData.width;
        this.height = imgData.height;
        this.data = imgData.data;
        this.clamp = cc.math.clamp;
    }

    contain(x, y) {
        return x >= 0 && x <= this.width - 1 && y >= 0 && y <= this.height - 1;
    }

    drawPoint(x, y, color) {
        if (debugUtils.debug) {
            debugUtils.checkInteger(x);
            debugUtils.checkInteger(y);
        }
        if (this.contain(x, y)) {
            let ny = this.height - y - 1;
            let idx = ny * this.width * 4 + x * 4;
            this.data[idx] = color.r;
            this.data[idx + 1] = color.g;
            this.data[idx + 2] = color.b;
            this.data[idx + 3] = color.a;
        }
    }

    drawLine(x1, y1, x2, y2, color) {
        let v1 = globalCache.get_v2();
        v1.x = x1;
        v1.y = y1;
        let v2 = globalCache.get_v2();
        v2.x = x2;
        v2.y = y2;
        if (this._clipLine(v1, v2)) {
            this._line(v1.x, v1.y, v2.x, v2.y, color);
        }
        globalCache.put_v2(v1);
        globalCache.put_v2(v2);
    }

    /**
     * 对 点 v1 v2 进行裁剪
     * @param v1
     * @param v2
     * @return {boolean} true 表示裁剪成功,更新v1 v2.  false 表示在区域外.
     */
    _clipLine(v1, v2) {
        let x1 = v1.x, y1 = v1.y, x2 = v2.x, y2 = v2.y;
        const xMax = this.width - 1;
        const yMax = this.height - 1;
        const xMin = 0;
        const yMin = 0;

        if (debugUtils.debug) {
            debugUtils.checkInteger(x1);
            debugUtils.checkInteger(y1);
            debugUtils.checkInteger(x2);
            debugUtils.checkInteger(y2);
        }

        // 都在上 下 左 右 一定不裁剪
        if (y1 > yMax && y2 > yMax) {
            return false;
        }
        if (y1 < yMin && y2 < yMin) {
            return false;
        }
        if (x1 < xMin && x2 < xMin) {
            return false;
        }
        if (x1 > xMax && x2 > xMax) {
            return false;
        }

        if (this.contain(x1, y1) && this.contain(x2, y2)) {
            // 不需要裁剪, 取原来的值
            return true;
        }

        if (x1 === x2) {
            // 垂直
            let min_y = Math.min(y1, y2);
            let max_y = Math.max(y1, y2);
            if (min_y > yMax || max_y < yMin) {
                // 视口外
                return false;
            }
            v1.y = this.clamp(y1, yMin, yMax);
            v2.y = this.clamp(y2, yMin, yMax);
            return true;
        }

        if (y1 === y2) {
            // 水平
            let min_x = Math.min(x1, x2);
            let max_x = Math.max(x1, x2);
            if (min_x > xMax || max_x < xMin) {
                return false;
            }
            v1.x = this.clamp(x1, xMin, xMax);
            v2.x = this.clamp(x2, xMin, xMax);
            return true;
        }

        //
        let left_x = Math.min(x1, x2);
        let left_y, right_x, right_y;
        let flip; // 表示是否交换
        if (left_x === x1) {
            left_y = y1;
            right_x = x2;
            right_y = y2;
            flip = false;
        } else {
            left_y = y2;
            right_x = x1;
            right_y = y1;
            flip = true;
        }

        // 令 x 分别为 rect.x 和  rect.xMax
        // P = (1-t) * A + t * B
        // t = (P - A) / (B - A)
        let p1 = (xMin - left_x) / (right_x - left_x);
        let p2 = (xMax - left_x) / (right_x - left_x);

        // 令 y分别为 rect.y 和  rect.yMax
        let p3 = (yMin - left_y) / (right_y - left_y);
        let p4 = (yMax - left_y) / (right_y - left_y);

        let p_l = Math.max(0, p1, Math.min(p3, p4));
        let p_r = Math.min(1, p2, Math.max(p3, p4));

        let l_x = Math.floor((1 - p_l) * left_x + p_l * right_x);
        let l_y = Math.floor((1 - p_l) * left_y + p_l * right_y);
        let r_x = Math.floor((1 - p_r) * left_x + p_r * right_x);
        let r_y = Math.floor((1 - p_r) * left_y + p_r * right_y);

        if (l_x > xMax || r_x < xMin) {
            return false;
        }

        let ret_min_y = Math.min(l_y, r_y);
        let ret_max_y = Math.max(l_y, r_y);

        if (ret_min_y > yMax || ret_max_y < yMin) {
            return false;
        }

        // 由于浮点数的原因.....
        l_x = this.clamp(l_x, xMin, xMax);
        r_x = this.clamp(r_x, xMin, xMax);
        l_y = this.clamp(l_y, yMin, yMax);
        r_y = this.clamp(r_y, yMin, yMax);

        if (flip) {
            v1.x = r_x;
            v1.y = r_y;
            v2.x = l_x;
            v2.y = l_y;
        } else {
            v1.x = l_x;
            v1.y = l_y;
            v2.x = r_x;
            v2.y = r_y;
        }

        return true;
    }

    /**
     * 绘制直线,但是不进行安全检查
     * @param x1
     * @param y1
     * @param x2
     * @param y2
     * @param color
     * @private
     */
    _line(x1, y1, x2, y2, color) {
        if (debugUtils.debug) {
            debugUtils.checkInteger(x1);
            debugUtils.checkInteger(y1);
            debugUtils.checkInteger(x2);
            debugUtils.checkInteger(y2);
        }

        // 不考虑裁剪
        if (x1 === x2) {
            // 垂直的
            let min = Math.min(y1, y2);
            let max = Math.max(y1, y2);
            for (let i = min; i <= max; ++i) {
                this._pixel(x1, i, color);
            }
            return;
        }

        if (y1 === y2) {
            // 水平
            let min = Math.min(x1, x2);
            let max = Math.max(x1, x2);
            for (let i = min; i <= max; ++i) {
                this._pixel(i, y1, color);
            }
            return;
        }

        // left(start) -> right(end)
        let k = (y2 - y1) / (x2 - x1);
        let el = 0;
        let b = y1 - k * x1;
        if (k >= 1 || k <= -1) {
            let min_y = Math.min(y1, y2);
            let max_y = Math.max(y1, y2);
            for (let i = min_y; i <= max_y; ++i) {
                let nx = (i - b) / k;
                let ix = Math.floor(nx);
                el += (nx - ix);
                if (el >= 0.5) {
                    ix++;
                    el -= 1;
                }
                this._pixel(ix, i, color);
            }
        } else {
            let min_x = Math.min(x1, x2);
            let max_x = Math.max(x1, x2);
            for (let i = min_x; i <= max_x; ++i) {
                let ny = k * i + b;
                let iy = Math.floor(ny);
                el += (ny - iy);
                if (el >= 0.5) {
                    iy++;
                    el -= 1;
                }
                this._pixel(i, iy, color);
            }
        }
    }

    /**
     * 绘制点. 但是不进行安全检查
     * @param x
     * @param y
     * @param color
     * @private
     */
    _pixel(x, y, color) {
        let ny = this.height - y - 1;
        let idx = ny * this.width * 4 + x * 4;
        this.data[idx] = color.r;
        this.data[idx + 1] = color.g;
        this.data[idx + 2] = color.b;
        this.data[idx + 3] = color.a;
    }

    clear() {
        this.data.fill(0, 0, this.data.length);
    }

    fillRect(x, y, w, h, color) {

        if (debugUtils.debug) {
            debugUtils.checkInteger(x);
            debugUtils.checkInteger(y);
            debugUtils.checkInteger(w);
            debugUtils.checkInteger(h);
        }

        let rect = globalCache.get_rect();
        rect.x = x;
        rect.y = y;
        rect.width = w;
        rect.height = h;

        let rect1 = globalCache.get_rect();
        rect1.x = rect1.y = 0;
        rect1.width = this.width - 1;
        rect1.height = this.height - 1;

        cc.Rect.intersection(rect, rect, rect1);

        // 相交有效
        if (rect.width > 0 && rect.height > 0) {
            let width = rect.width;
            let height = rect.height;
            let nx = rect.x;
            let ny = rect.y;
            for (let i = 0; i < height; ++i) {
                for (let j = 0; j < width; ++j) {
                    this._pixel(nx + j, ny + i, color);
                }
            }
        }
        globalCache.put_rect(rect);
        globalCache.put_rect(rect1);
    }

    drawRect(x, y, w, h, color) {
        this.drawLine(x, y, x + w, y, color);
        this.drawLine(x, y, x, y + h, color);
        this.drawLine(x + w, y + h, x + w, y, color);
        this.drawLine(x + w, y + h, x, y + h, color);
    }

    drawTriangle(x1, y1, x2, y2, x3, y3, color) {
        this.drawLine(x1, y1, x2, y2, color);
        this.drawLine(x2, y2, x3, y3, color);
        this.drawLine(x3, y3, x1, y1, color);
    }

    /**
     * 绘制平顶三角形
     * 前提条件:
     * 1. 逆时针
     * 2. y1 === y3
     * @param x1
     * @param y1
     * @param x2
     * @param y2
     * @param x3
     * @param y3
     * @param color
     */
    _fillTopFlatTriangle(x1,y1,x2,y2,x3,y3,color){
        let xMax = this.width - 1;
        let yMax = this.height - 1;
        if (debugUtils.debug) {
            debugUtils.checkInteger(x1);
            debugUtils.checkInteger(y1);
            debugUtils.checkInteger(x2);
            debugUtils.checkInteger(y2);
            debugUtils.checkInteger(x3);
            debugUtils.checkInteger(y3);
        }
        let y_start = Math.min(yMax,y1);
        let y_end = Math.max(0,y2);
        let dis_left_x = x2 - x1;
        let dis_left_y = y2 - y1;
        let dis_right_x = x2 - x3;
        for(let j = y_start;j >= y_end; --j){
            let t = (j - y1) / dis_left_y;
            let x_start = x1 + dis_left_x * t;
            let x_end = x3 + dis_right_x * t;

            if(x_start > xMax || x_end < 0){
                continue;
            }

            // 为了避免裁剪
            x_start = this.clamp(x_start,0,xMax);
            x_end = this.clamp(x_end,0,xMax);
            this._line(Math.floor(x_start),j,Math.round(x_end),j,color);
        }
    }

    /**
     * 绘制平底三角形
     * 前提条件:
     * 1. 逆时针
     * 2. y2 === y3
     * @param x1
     * @param y1
     * @param x2
     * @param y2
     * @param x3
     * @param y3
     * @param color
     * @private
     */
    _fillBottomFlatTriangle(x1,y1,x2,y2,x3,y3,color){
        let xMax = this.width - 1;
        let yMax = this.height - 1;
        let y_start = Math.min(yMax,y1);
        let y_end = Math.max(0,y2);
        let dis_left_x = x2 - x1;
        let dis_left_y = y2 - y1;
        let dis_right_x = x3 - x1;
        let dis_right_y = y3 - y1;

        //  先绘制上半部分
        for(let j = y_start; j >= y_end; --j){
            let t_left = (j - y1) / dis_left_y;
            let t_right = (j - y1) / dis_right_y;
            let x_start  = x1 + dis_left_x * t_left;
            let x_end = x1 + dis_right_x * t_right;

            if(x_start > xMax || x_end < 0){
                continue;
            }

            // 为了避免裁剪
            x_start = this.clamp(x_start,0,xMax);
            x_end = this.clamp(x_end,0,xMax);
            this._line(Math.floor(x_start),j,Math.round(x_end),j,color);
        }
    }

    fillTriangle(x1, y1, x2, y2, x3, y3, color) {

        let xMax = this.width - 1;
        let yMax = this.height - 1;

        // 判断是否需要进行绘制???
        if ((x1 < 0 && x2 < 0 && x3 < 0) ||
            (x1 > xMax && x2 > xMax && x3 > xMax) ||
            (y1 < 0 && y2 < 0 && y3 < 0) ||
            (y1 > yMax && y2 > yMax && y3 > yMax)) {
            return;
        }

        if (debugUtils.debug) {
            debugUtils.checkInteger(x1);
            debugUtils.checkInteger(y1);
            debugUtils.checkInteger(x2);
            debugUtils.checkInteger(y2);
            debugUtils.checkInteger(x3);
            debugUtils.checkInteger(y3);
        }

        // 判断是否是逆时针. 如果顺时针需要调整顺序
        if (!this._isCCW(x1, y1, x2, y2, x3, y3)) {
            // 调整成逆时针
            let tx = x2;
            let ty = y2;
            x2 = x3;
            y2 = y3;
            x3 = tx;
            y3 = ty;
        }

        // 调整顶点顺序,让y1最大
        if(y1 < y2 || y1 < y3){
            if(y2 < y3){
                let tx = x1;
                let ty = y1;
                x1 = x3;
                y1 = y3;
                x3 = x2;
                y3 = y2;
                x2 = tx;
                y2 = ty;
            }else{
                // y2 >= y3
                let tx = x1;
                let ty = y1;
                x1 = x2;
                y1 = y2;
                x2 = x3;
                y2 = y3;
                x3 = tx;
                y3 = ty;
            }
        }

        // 判断是否是平顶三角形
        if (y1 === y3) {
            this._fillTopFlatTriangle(x1,y1,x2,y2,x3,y3,color);
        } else {
            // 判断是左边长还是右边长
            // 并且拆分成两个三角形
            if(y2  > y3){
                // 右边长. 求点D
                let yd = y2;
                let t = (yd - y1) / (y3 - y1);
                let xd = x1 + t * (x3 - x1);
                this._fillBottomFlatTriangle(x1,y1,x2,y2,Math.floor(xd),yd,color);
                this._fillTopFlatTriangle(x2,y2,x3,y3,Math.floor(xd),yd,color);
            }else{
                // 平底或左边长
                if(y2 === y3){
                    this._fillBottomFlatTriangle(x1,y1,x2,y2,x3,y3,color);
                }else{
                    let yd = y3;
                    let t = (yd - y1) / (y2 - y1);
                    let xd = x1 + t * (x2 - x1);
                    this._fillBottomFlatTriangle(x1,y1,Math.floor(xd),yd,x3,y3,color);
                    this._fillTopFlatTriangle(Math.floor(xd),yd,x2,y2,x3,y3,color);
                }
            }
        }
    }

    /**
     *  判断是否是逆时针
     * @param x1
     * @param y1
     * @param x2
     * @param y2
     * @param x3
     * @param y3
     * @return {boolean}
     * @private
     */
    _isCCW(x1, y1, x2, y2, x3, y3) {
        let ab = globalCache.get_v2();
        ab.x = x2 - x1;
        ab.y = y2 - y1;

        let ac = globalCache.get_v2();
        ac.x = x3 - x1;
        ac.y = y3 - y1;

        let cross = ab.x * ac.y - ab.y * ac.x;
        globalCache.put_v2(ab);
        globalCache.put_v2(ac);
        return cross > 0;
    }

    drawArrow(x1,y1,x2,y2,color){

        let nba = globalCache.get_v2();
        nba.x = x1 - x2;
        nba.y = y1 - y2;
        cc.Vec2.normalize(nba,nba);

        let ncd = globalCache.get_v2();
        ncd.x = -nba.y;
        ncd.y = nba.x;

        const Len = 8;
        const Rad = 30 * Math.PI/180;
        const cos = Math.cos(Rad);

        let ebLen = Len * cos;

        let e = globalCache.get_v2();
        e.x = nba.x * ebLen + x2;
        e.y = nba.y * ebLen + y2;


        let ceLen = Len * Math.sin(Rad);
        let d = globalCache.get_v2();
        d.x = Math.floor(e.x + ceLen * ncd.x);
        d.y = Math.floor(e.y + ceLen * ncd.y);

        let c = globalCache.get_v2();
        c.x = Math.floor(e.x - ceLen * ncd.x);
        c.y = Math.floor(e.y - ceLen * ncd.y);

        this.drawLine(x1,y1,x2,y2,color);
        this.drawLine(x2,y2,c.x,c.y,color);
        this.drawLine(x2,y2,d.x,d.y,color);
    }
}
export default Graphics2d;
